import Taro from '@tarojs/taro'
import { useState, useEffect, useRef, useCallback } from 'react'
import { View, Text, Button, Input } from '@tarojs/components'
import { Cell, Dialog, Toast } from '@taroify/core'
import { OtherPay, Scan } from '@taroify/icons'
import JSSHA from 'jssha'
import base32Decode from 'base32-decode'
import './index.scss'
import { generateTOTP, getBatchTOTP, syncTOTPAccounts, getUserTOTPAccounts, getServerTimeInfo } from '../api/totp'

// 当前缓存版本，每次更改数据结构时需要递增
const CACHE_VERSION = '1.0.0'

// 清理缓存的辅助函数
const clearCacheIfNeeded = () => {
  try {
    // 检查缓存版本
    const cacheVersion = Taro.getStorageSync('cacheVersion')

    if (cacheVersion !== CACHE_VERSION) {
      console.log('缓存版本不匹配或首次使用，清理缓存', cacheVersion, CACHE_VERSION)
      // 清理相关缓存
      Taro.removeStorageSync('totpAccounts')
      Taro.removeStorageSync('serverTimeOffset')
      // 设置新版本
      Taro.setStorageSync('cacheVersion', CACHE_VERSION)
    }
  } catch (error) {
    console.error('缓存检查失败，重置所有缓存', error)
    try {
      Taro.clearStorageSync()
      Taro.setStorageSync('cacheVersion', CACHE_VERSION)
    } catch (e) {
      console.error('清理缓存失败', e)
    }
  }
}

// 检查云环境是否正常
const checkCloudEnv = async () => {
  try {
    const result = await getServerTimeInfo()
    if (result && result.result && result.result.success) {
      console.log('云环境检查通过')
      return true
    }
    console.error('云环境响应格式异常', result)
    return false
  } catch (error) {
    console.error('云环境检查失败', error)
    return false
  }
}

// 节流函数，防止短时间内多次调用API
const throttle = (fn, delay) => {
  let timer = null
  let lastTime = 0
  return function (...args) {
    const currentTime = Date.now()
    if (currentTime - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = currentTime
    } else if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args)
        lastTime = Date.now()
        timer = null
      }, delay - (currentTime - lastTime))
    }
  }
}

const TotpGenerator = () => {
  const [accounts, setAccounts] = useState([])
  const [currentCodes, setCurrentCodes] = useState({})
  const [timeRemaining, setTimeRemaining] = useState(30)
  const [serverTimeOffset, setServerTimeOffset] = useState(0) // 服务器时间与本地时间的偏差
  const [periodEnd, setPeriodEnd] = useState(0) // 当前验证码周期结束时间
  const [showAddDialog, setShowAddDialog] = useState(false)
  const [showDeleteDialog, setShowDeleteDialog] = useState(false)
  const [showEditDialog, setShowEditDialog] = useState(false)
  const [accountToDelete, setAccountToDelete] = useState(null)
  const [accountToEdit, setAccountToEdit] = useState(null)
  const [newAccount, setNewAccount] = useState({
    name: '',
    secret: '',
    issuer: ''
  })
  const [toastConfig, setToastConfig] = useState({ open: false, message: '', type: 'success' })
  const [isLoading, setIsLoading] = useState(true)
  const [requestId, setRequestId] = useState(0) // 请求ID跟踪
  const [lastRequestTime, setLastRequestTime] = useState(0) // 最后请求时间
  const timerRef = useRef(null)
  const circleRef = useRef(null)
  const initialLoadDoneRef = useRef(false) // 追踪初始加载是否完成
  const [isRefreshing, setIsRefreshing] = useState(false)

  // 使用useCallback包装API调用函数
  const fetchCurrentTOTPImpl = useCallback(
    async (accountsList = accounts, newAccountData = null) => {
      if (accountsList.length === 0) return

      // 生成当前请求ID
      const currentRequestId = requestId + 1
      setRequestId(currentRequestId)

      setIsLoading(true)
      try {
        const accountIds = accountsList.map((acc) => acc.id)
        const response = await getBatchTOTP(accountIds, newAccountData)

        // 确保只处理最新请求的响应
        if (currentRequestId !== requestId) {
          console.log('忽略过期请求响应')
          return
        }

        // 云函数返回格式适配
        if (response && response.result && response.result.success && response.result.data) {
          const { codes, expiresAts, currentTime } = response.result.data

          // 避免不必要的状态更新
          if (JSON.stringify(currentCodes) !== JSON.stringify(codes)) {
            console.log('更新验证码:', codes)
            setCurrentCodes(codes) // 确保这里的状态更新被执行
          }

          // 计算服务器时间与本地时间的偏差（更精确的计算）
          if (currentTime) {
            const localTime = Math.floor(Date.now() / 1000)
            const offset = currentTime - localTime

            // 保存时间偏移到本地存储，确保不同设备间的一致性
            try {
              Taro.setStorageSync('serverTimeOffset', offset.toString())
            } catch (error) {
              console.error('保存服务器时间偏移失败:', error)
            }

            // 如果偏移变化较大，才更新状态
            if (Math.abs(offset - serverTimeOffset) > 1) {
              console.log(`更新服务器时间偏差: ${offset}秒 (之前: ${serverTimeOffset}秒)`)
              setServerTimeOffset(offset)
            }
          }

          // 设置最早的过期时间作为当前周期结束时间
          if (expiresAts && Object.keys(expiresAts).length > 0) {
            const earliestExpiry = Math.min(...Object.values(expiresAts))
            setPeriodEnd(earliestExpiry)
            console.log(`当前验证码周期结束时间: ${earliestExpiry}`)

            // 立即更新剩余时间
            const localTime = Math.floor(Date.now() / 1000)
            const remaining = Math.max(0, earliestExpiry - (localTime + serverTimeOffset))
            setTimeRemaining(remaining)
            updateCircleProgress(remaining)
          }
        } else {
          throw new Error('获取验证码返回格式错误')
        }
      } catch (error) {
        console.error('获取当前验证码失败:', error)
        if (currentRequestId === requestId) {
          // 只有最新请求的错误才处理
          showToast('使用本地备用验证码', 'error')
          // 失败后尝试本地生成
          fallbackGenerateTOTP()
        }
      } finally {
        if (currentRequestId === requestId) {
          // 只有最新请求才更新加载状态
          setIsLoading(false)
        }
      }
    },
    [accounts, currentCodes, requestId, serverTimeOffset]
  )

  // 创建节流版本的API调用函数
  const fetchCurrentTOTP = useCallback(throttle(fetchCurrentTOTPImpl, 2000), [fetchCurrentTOTPImpl])

  // 从服务器获取新的验证码
  const fetchTOTPImpl = useCallback(async () => {
    if (accounts.length === 0) return

    // 生成当前请求ID
    const currentRequestId = requestId + 1
    setRequestId(currentRequestId)

    // 记录请求时间
    const requestTime = Date.now()
    if (requestTime - lastRequestTime < 1000) {
      console.log('忽略过于频繁的请求')
      return
    }
    setLastRequestTime(requestTime)

    console.log('开始获取新验证码，当前时间:', new Date().toLocaleTimeString())

    // 只在真正需要更新时才设置加载状态，避免不必要的UI闪烁
    const timeToPeriodEnd = periodEnd - Math.floor(Date.now() / 1000) - serverTimeOffset
    const needsUpdate = timeToPeriodEnd <= 0 || Object.keys(currentCodes).length !== accounts.length

    if (needsUpdate) {
      setIsLoading(true)
    }

    try {
      const accountIds = accounts.map((acc) => acc.id)
      const response = await generateTOTP({ accountIds })

      // 确保只处理最新请求的响应
      if (currentRequestId !== requestId) {
        console.log('忽略过期请求响应')
        return
      }

      // 云函数返回格式适配
      if (response && response.result && response.result.success && response.result.data) {
        const { codes, periodEnd: newPeriodEnd, currentTime } = response.result.data

        // 同步服务器时间（每次请求都更新时间偏移）
        if (currentTime) {
          const localTime = Math.floor(Date.now() / 1000)
          const offset = currentTime - localTime

          // 保存最新的时间偏移
          try {
            Taro.setStorageSync('serverTimeOffset', offset.toString())
          } catch (error) {
            console.error('保存服务器时间偏移失败:', error)
          }

          // 如果偏移变化较大，才更新状态
          if (Math.abs(offset - serverTimeOffset) > 1) {
            console.log(`更新服务器时间偏差: ${offset}秒 (之前: ${serverTimeOffset}秒)`)
            setServerTimeOffset(offset)
          }
        }

        // 避免不必要的状态更新
        if (codes && Object.keys(codes).length > 0 && JSON.stringify(currentCodes) !== JSON.stringify(codes)) {
          console.log('生成新验证码成功:', codes)
          setCurrentCodes({ ...codes }) // 使用解构确保创建新对象触发更新
        } else if (Object.keys(currentCodes).length === 0) {
          // 如果没有验证码，但服务端返回为空，尝试使用本地生成
          console.log('服务端返回验证码为空，使用本地备用验证码')
          fallbackGenerateTOTP()
        }

        // 更新周期结束时间
        if (newPeriodEnd) {
          setPeriodEnd(newPeriodEnd)
          console.log(`新验证码周期结束时间: ${newPeriodEnd}, 本地时间: ${Math.floor(Date.now() / 1000)}`)

          // 立即更新剩余时间
          const localTime = Math.floor(Date.now() / 1000)
          const remaining = Math.max(0, newPeriodEnd - (localTime + serverTimeOffset))
          setTimeRemaining(remaining)
          updateCircleProgress(remaining)
        }
      } else {
        console.log('响应格式不符合预期，使用备用方案')
        // 返回格式不对也使用备用方案
        fallbackGenerateTOTP()
        throw new Error('生成验证码返回格式错误')
      }
    } catch (error) {
      console.error('生成新验证码失败:', error)
      if (currentRequestId === requestId) {
        // 只有最新请求的错误才处理
        showToast('使用本地备用验证码', 'error')
        // 失败后尝试本地生成
        fallbackGenerateTOTP()
      }
    } finally {
      if (currentRequestId === requestId) {
        // 始终更新加载状态，确保loading状态不会一直显示
        setIsLoading(false)
      }
    }
  }, [accounts, currentCodes, lastRequestTime, periodEnd, requestId, serverTimeOffset])

  // 创建节流版本的生成验证码函数，并增加延迟时间以减少API调用
  const fetchTOTP = useCallback(throttle(fetchTOTPImpl, 3000), [fetchTOTPImpl])

  useEffect(() => {
    // 加载已保存的账户
    if (!initialLoadDoneRef.current) {
      // 尝试从本地存储获取服务器时间偏移
      try {
        const savedOffset = Taro.getStorageSync('serverTimeOffset')
        if (savedOffset) {
          const offset = parseInt(savedOffset, 10)
          setServerTimeOffset(offset)
          console.log(`从本地存储恢复服务器时间偏移: ${offset}秒`)
        }
      } catch (error) {
        console.error('获取服务器时间偏移失败:', error)
      }

      loadAccounts()
      initialLoadDoneRef.current = true
    }

    // 获取当前时间在30秒周期中的位置（考虑服务器时间偏移）
    const initialNow = Math.floor(Date.now() / 1000) + serverTimeOffset
    const initialRemaining = 30 - (initialNow % 30)
    setTimeRemaining(initialRemaining)

    // 组件挂载后更新进度
    setTimeout(() => {
      updateCircleProgress(initialRemaining)
    }, 100)

    // 开始计时，每秒更新剩余时间
    const timer = setInterval(() => {
      // 如果正在加载且没有验证码，则暂停倒计时
      if (isLoading && Object.keys(currentCodes).length === 0) {
        // 暂停倒计时，但仍然检查加载超时
        if (lastRequestTime > 0 && Date.now() - lastRequestTime > 5000) {
          console.log('加载时间过长，使用本地验证码')
          fallbackGenerateTOTP(accounts)
          setIsLoading(false)
        }
        return
      }

      // 计算剩余时间（使用服务器时间校准）
      const now = Math.floor(Date.now() / 1000) + serverTimeOffset
      let remaining

      if (periodEnd > 0) {
        // 如果有周期结束时间，使用它计算剩余时间
        remaining = Math.max(0, periodEnd - now)
      } else {
        // 否则使用本地计算
        remaining = 30 - (now % 30)
      }

      // 检查是否到了周期的临界点
      const previousRemaining = timeRemaining

      // 更新剩余时间状态
      setTimeRemaining(remaining)

      // 更新圆环进度
      updateCircleProgress(remaining)

      // 当从1秒变为0秒，或者从30秒变为29秒时，强制刷新验证码
      const isNewCycle =
        (previousRemaining === 1 && remaining === 0) ||
        (previousRemaining === 30 && remaining === 29) ||
        remaining === 0

      if (isNewCycle) {
        console.log('周期临界点，强制刷新验证码', remaining)

        // 在本地使用fallback先快速刷新验证码，保证UI立即更新
        fallbackGenerateTOTP(accounts)

        // 如果当前不在加载状态，才从服务器获取验证码，避免并发请求
        if (!isLoading) {
          fetchTOTP()
        }
      }

      // 如果有账户但没有验证码，尝试生成验证码
      if (accounts.length > 0 && Object.keys(currentCodes).length === 0) {
        console.log('有账户但没有验证码，尝试生成')
        fallbackGenerateTOTP(accounts)
      }

      // 如果加载时间超过5秒，强制使用本地验证码并重置加载状态
      if (isLoading && lastRequestTime > 0 && Date.now() - lastRequestTime > 5000) {
        console.log('加载时间过长，使用本地验证码')
        fallbackGenerateTOTP(accounts)
        setIsLoading(false)
      }
    }, 1000)

    timerRef.current = timer

    return () => {
      clearInterval(timer)
    }
  }, [accounts, currentCodes, serverTimeOffset, periodEnd, fetchTOTP, timeRemaining, isLoading, lastRequestTime])

  // 更新圆环进度
  const updateCircleProgress = (seconds) => {
    if (circleRef.current) {
      // 如果正在加载且没有验证码，则显示加载状态
      const progress =
        isLoading && Object.keys(currentCodes).length === 0
          ? 100 // 显示完整圆环表示加载中
          : (seconds / 30) * 100
      circleRef.current.style.setProperty('--progress', `${progress}%`)
    }
  }

  // 加载保存的账户
  const loadAccounts = async () => {
    if (isLoading && initialLoadDoneRef.current) {
      console.log('已有请求正在进行，跳过')
      return
    }

    try {
      setIsLoading(true)
      let accountsData = []

      // 1. 先尝试读取本地存储的账户
      try {
        const savedAccounts = Taro.getStorageSync('totpAccounts')
        if (savedAccounts) {
          accountsData = JSON.parse(savedAccounts)
          console.log('从本地存储加载账户:', accountsData.length)
        }
      } catch (parseError) {
        console.error('解析本地账户数据失败，重置数据:', parseError)
        // 数据格式错误，清除该存储
        Taro.removeStorageSync('totpAccounts')
        accountsData = []
      }

      // 2. 如果本地没有账户或账户数量为0，尝试从云端加载
      if (!accountsData || accountsData.length === 0) {
        try {
          console.log('尝试从云端恢复账户信息')
          const res = await getUserTOTPAccounts()
          if (res && res.result && res.result.success && res.result.data.accounts) {
            accountsData = res.result.data.accounts
            console.log('从云端恢复账户成功:', accountsData.length)

            // 将恢复的账户保存到本地
            Taro.setStorageSync('totpAccounts', JSON.stringify(accountsData))
          }
        } catch (error) {
          console.error('从云端恢复账户失败:', error)
          // 显示提示
          showToast('从云端恢复账户失败，请检查网络', 'error')
        }
      }

      // 设置账户数据
      setAccounts(accountsData)

      // 3. 如果有账户，生成验证码
      if (accountsData.length > 0) {
        // 初始先生成本地验证码，保证即使API失败也有验证码显示
        console.log('有账户，立即生成本地验证码')
        fallbackGenerateTOTP(accountsData)

        // 同步账户到云端（确保云端数据是最新的）
        try {
          await syncTOTPAccounts(accountsData)
          console.log('账户信息已同步到服务器')
        } catch (error) {
          console.error('同步账户信息失败:', error)
        }

        // 从服务器获取验证码（包含时间信息）
        await fetchCurrentTOTP(accountsData, accountsData.length > 0 ? accountsData[accountsData.length - 1] : null)
      } else {
        console.log('没有找到任何TOTP账户')
      }

      setIsLoading(false)

      // 强制再次生成验证码（以防前面的尝试失败）
      if (accountsData.length > 0) {
        setTimeout(() => {
          if (Object.keys(currentCodes).length === 0) {
            console.log('验证码为空，再次尝试生成')
            fallbackGenerateTOTP(accountsData)
          }
        }, 1000)
      }
    } catch (e) {
      console.error('加载TOTP账户失败', e)
      showToast('加载账户失败', 'error')
      setIsLoading(false)
    }
  }

  // 本地生成验证码（仅作为API失败的备用方案）
  const fallbackGenerateTOTP = useCallback(
    (accountsList = accounts) => {
      if (accountsList.length === 0) return

      const codes = {}
      accountsList.forEach((account) => {
        try {
          const code = generateTOTPCode(account.secret)
          // 确保使用字符串 ID
          codes[String(account.id)] = code
          console.log(`本地备用: 为账户 ${account.name} (ID: ${account.id}) 生成TOTP: ${code}`)
        } catch (e) {
          console.error(`本地备用: 为账户 ${account.name} 生成TOTP失败:`, e)
          codes[String(account.id)] = '错误'
        }
      })

      console.log('本地备用: 设置验证码:', codes)
      setCurrentCodes({ ...codes }) // 使用解构创建新对象触发React更新
    },
    [accounts, serverTimeOffset]
  )

  // 计算TOTP验证码（仅作为本地备用方案使用）
  const generateTOTPCode = (secret) => {
    try {
      if (!secret) {
        console.error('生成TOTP码失败: secret为空')
        return '错误-空'
      }

      // 移除空格和换行符
      const sanitizedSecret = secret.replace(/\s/g, '').toUpperCase()
      console.log('处理后的secret:', sanitizedSecret)

      if (sanitizedSecret.length < 6) {
        console.error('生成TOTP码失败: secret太短')
        return '错误-短'
      }

      // 解码Base32
      try {
        const decoded = base32Decode(sanitizedSecret, 'RFC4648')

        // 计算当前时间戳 (使用服务器时间偏移校准)
        const epoch = Math.floor(Date.now() / 1000) + serverTimeOffset
        let timeCounter = Math.floor(epoch / 30)

        console.log(
          `使用校准后的时间戳生成验证码，时间戳:${epoch}，偏移:${serverTimeOffset}秒，TOTP区间:${timeCounter}`
        )

        // 转换为字节数组
        const timeByteArray = new Uint8Array(8)
        for (let i = 0; i < 8; i++) {
          timeByteArray[7 - i] = timeCounter & 0xff
          timeCounter = timeCounter >> 8
        }

        // 使用HMAC-SHA1计算哈希
        const shaObj = new JSSHA('SHA-1', 'ARRAYBUFFER')
        shaObj.setHMACKey(decoded, 'ARRAYBUFFER')
        shaObj.update(timeByteArray.buffer)
        const hmac = shaObj.getHMAC('ARRAYBUFFER')

        // 根据RFC获取截断索引
        const offset = new Uint8Array(hmac)[19] & 0xf

        // 从哈希中提取4字节
        const bytes = new Uint8Array(hmac)
        const binary =
          ((bytes[offset] & 0x7f) << 24) |
          ((bytes[offset + 1] & 0xff) << 16) |
          ((bytes[offset + 2] & 0xff) << 8) |
          (bytes[offset + 3] & 0xff)

        // 获取6位数字的TOTP码
        const otp = binary % 1000000

        // 确保是6位数字（前导零）
        const code = otp.toString().padStart(6, '0')
        console.log('成功生成TOTP码:', code)
        return code
      } catch (decodeError) {
        console.error('Base32解码失败:', decodeError)
        return '解码错'
      }
    } catch (error) {
      console.error('生成TOTP码失败:', error)
      return '错误'
    }
  }

  // 手动刷新验证码
  const handleRefresh = () => {
    if (isRefreshing) return // 避免重复请求

    // 立即使用本地生成验证码更新UI
    fallbackGenerateTOTP(accounts)

    setIsRefreshing(true)
    refreshCodes().finally(() => {
      setIsRefreshing(false)
    })
  }

  // 刷新TOTP码的主函数
  const refreshCodes = async () => {
    console.log('Manual refresh triggered')
    // 尝试本地生成验证码以快速显示
    fallbackGenerateTOTP(accounts)
    // 为手动刷新重置请求计时器，确保立即执行
    setLastRequestTime(0)
    await fetchTOTP()
    showToast('验证码已刷新', 'success')
  }

  // 处理添加账户
  const handleAddAccount = async () => {
    try {
      // 验证输入
      if (!newAccount.name || !newAccount.secret) {
        showToast('名称和密钥不能为空', 'error')
        return
      }

      // 验证secret格式
      try {
        base32Decode(newAccount.secret.replace(/\s/g, ''), 'RFC4648')
      } catch (error) {
        showToast('无效的密钥格式', 'error')
        return
      }

      // 创建新账户
      const id = `totp_${Date.now()}`
      const cleanedSecret = newAccount.secret.replace(/\s/g, '').toUpperCase()
      const newAccountData = {
        ...newAccount,
        id,
        secret: cleanedSecret,
        created: new Date().toISOString()
      }

      // 更新状态
      const updatedAccounts = [...accounts, newAccountData]
      await saveAccounts(updatedAccounts)

      // 关闭对话框
      setNewAccount({ name: '', secret: '', issuer: '' })
      setShowAddDialog(false)

      showToast('账户已添加', 'success')

      // 尝试为新添加的账户生成TOTP验证码
      try {
        // 生成API验证码
        await fetchCurrentTOTP(updatedAccounts, newAccountData)
      } catch (err) {
        console.error('新账户验证码生成失败', err)
        // 在API失败时使用本地生成的验证码
        fallbackGenerateTOTP(updatedAccounts)
      }
    } catch (e) {
      console.error('添加账户失败', e)
      showToast('添加账户失败', 'error')
    }
  }

  // 渲染刷新按钮
  const renderRefreshButton = () => {
    return (
      <View className='refresh-button' onClick={handleRefresh}>
        <Text className={`refresh-icon ${isLoading ? 'refreshing' : ''}`}>↻</Text>
      </View>
    )
  }

  // 渲染账户列表
  const renderAccounts = () => {
    if (accounts.length === 0) {
      return (
        <View className='empty-state'>
          <Text className='empty-text'>未添加验证器账户</Text>
          <Text className='empty-desc'>点击下方按钮添加账号或扫描二维码</Text>
        </View>
      )
    }

    return (
      <View className='accounts-list'>
        {accounts.map((account) => {
          const accountId = String(account.id)
          const code = currentCodes[accountId]
          const formattedCode = formatTOTPCode(code || '******')
          const showLoading = isLoading && !code

          return (
            <View key={account.id} className='account-item'>
              <View className='account-info'>
                <Text className='account-name'>{account.name}</Text>
                {account.issuer && <Text className='account-issuer'>{account.issuer}</Text>}
              </View>
              <View
                onClick={() => {
                  if (code) {
                    Taro.setClipboardData({
                      data: code,
                      success: () => {
                        showToast('已复制到剪贴板', 'success')
                      }
                    })
                  }
                }}>
                {showLoading ? (
                  <Text className='loading-code'>加载中...</Text>
                ) : (
                  <Text className='account-code'>{formattedCode}</Text>
                )}
              </View>
            </View>
          )
        })}
      </View>
    )
  }

  // 删除账户
  // 扫描二维码
  const handleScanQR = () => {
    Taro.scanCode({
      scanType: ['qrCode'],
      success: (res) => {
        try {
          if (res.result && res.result.startsWith('otpauth://totp/')) {
            const url = new URL(res.result)
            const pathParts = url.pathname.split('/')
            const labelParts = pathParts[2].split(':')

            let issuer = ''
            let name = ''

            if (labelParts.length > 1) {
              issuer = labelParts[0]
              name = labelParts[1]
            } else {
              name = labelParts[0]
              issuer = url.searchParams.get('issuer') || ''
            }

            const secret = url.searchParams.get('secret')

            if (secret) {
              setNewAccount({
                name,
                secret,
                issuer
              })
              setShowAddDialog(true)
            } else {
              showToast('无效的QR码', 'error')
            }
          } else {
            showToast('不是有效的TOTP二维码', 'error')
          }
        } catch (e) {
          console.error('解析QR码失败:', e)
          showToast('解析QR码失败', 'error')
        }
      },
      fail: () => {
        showToast('扫描取消或失败', 'error')
      }
    })
  }

  // 显示Toast提示
  const showToast = (message, type = 'success') => {
    setToastConfig({
      open: true,
      message,
      type
    })

    setTimeout(() => {
      setToastConfig({ ...toastConfig, open: false })
    }, 2000)
  }

  // 保存账户到本地存储
  const saveAccounts = async (updatedAccounts) => {
    try {
      Taro.setStorageSync('totpAccounts', JSON.stringify(updatedAccounts))
      setAccounts(updatedAccounts)

      // 同步账户信息到服务器
      try {
        await syncTOTPAccounts(updatedAccounts)
        console.log('账户信息已同步到服务器')
      } catch (error) {
        console.error('同步账户信息失败:', error)
        // 同步失败时显示提示
        showToast('数据已保存到本地，但同步到云端失败', 'warning')

        // 如果同步到云端失败，设置一个标记，下次启动时再次尝试同步
        try {
          Taro.setStorageSync('needSync', 'true')
        } catch (e) {
          console.error('设置同步标记失败:', e)
        }
      }
    } catch (e) {
      console.error('保存TOTP账户失败', e)
      showToast('保存账户失败', 'error')

      // 尝试使用备份方法保存
      try {
        // 先备份到其他键名下
        Taro.setStorageSync('totpAccounts_backup', JSON.stringify(updatedAccounts))
        showToast('已创建数据备份', 'success')
      } catch (backupError) {
        console.error('备份账户数据失败:', backupError)
      }
    }
  }

  // 处理编辑账户
  const handleEditAccount = (account) => {
    setAccountToEdit({ ...account })
    setShowEditDialog(true)
  }

  // 处理删除账户
  const handleDeleteAccount = (account) => {
    setAccountToDelete(account)
    setShowDeleteDialog(true)
  }

  // 确认编辑账户
  const confirmEditAccount = async () => {
    try {
      // 验证输入
      if (!accountToEdit.name || !accountToEdit.secret) {
        showToast('名称和密钥不能为空', 'error')
        return
      }

      // 验证secret格式
      try {
        base32Decode(accountToEdit.secret.replace(/\s/g, ''), 'RFC4648')
      } catch (error) {
        showToast('无效的密钥格式', 'error')
        return
      }

      // 更新账户数据
      const cleanedSecret = accountToEdit.secret.replace(/\s/g, '').toUpperCase()
      const updatedAccount = {
        ...accountToEdit,
        secret: cleanedSecret
      }

      // 更新状态
      const updatedAccounts = accounts.map((acc) => (acc.id === updatedAccount.id ? updatedAccount : acc))

      await saveAccounts(updatedAccounts)
      setShowEditDialog(false)
      showToast('账户已更新', 'success')

      // 更新验证码
      await fetchTOTP()
    } catch (e) {
      console.error('编辑账户失败', e)
      showToast('编辑账户失败', 'error')
    }
  }

  // 确认删除账户
  const confirmDeleteAccount = async () => {
    try {
      const updatedAccounts = accounts.filter((acc) => acc.id !== accountToDelete.id)
      await saveAccounts(updatedAccounts)
      setShowDeleteDialog(false)
      showToast('账户已删除', 'success')

      // 更新验证码列表
      setCurrentCodes((prev) => {
        const newCodes = { ...prev }
        delete newCodes[accountToDelete.id]
        return newCodes
      })
    } catch (e) {
      console.error('删除账户失败', e)
      showToast('删除账户失败', 'error')
    }
  }

  // 格式化TOTP验证码，用于显示
  const formatTOTPCode = (code) => {
    if (!code) return ''
    // 格式化为 xxx xxx 形式
    return code.length >= 6 ? `${code.slice(0, 3)} ${code.slice(3)}` : code
  }

  // 在组件加载后立即同步时间
  useEffect(() => {
    // 立即与服务器同步时间
    const syncTime = async () => {
      try {
        // 检查并清理缓存
        clearCacheIfNeeded()

        // 检查是否有未同步的数据
        try {
          const needSync = Taro.getStorageSync('needSync')
          if (needSync === 'true') {
            console.log('发现未同步的数据，尝试重新同步')
            const savedAccounts = Taro.getStorageSync('totpAccounts')
            if (savedAccounts) {
              const accountsData = JSON.parse(savedAccounts)
              // 尝试重新同步到云端
              try {
                await syncTOTPAccounts(accountsData)
                console.log('延迟同步成功')
                // 清除同步标记
                Taro.removeStorageSync('needSync')
              } catch (syncError) {
                console.error('延迟同步失败:', syncError)
              }
            }
          }
        } catch (syncCheckError) {
          console.error('检查同步状态失败:', syncCheckError)
        }

        // 检查是否有备份数据需要恢复
        try {
          const backupData = Taro.getStorageSync('totpAccounts_backup')
          const mainData = Taro.getStorageSync('totpAccounts')

          if (backupData && (!mainData || mainData.length < 10)) {
            console.log('主数据异常，从备份恢复')
            Taro.setStorageSync('totpAccounts', backupData)
            showToast('已从备份恢复数据', 'success')
          }

          // 清理备份数据
          if (mainData && mainData.length > 10) {
            Taro.removeStorageSync('totpAccounts_backup')
          }
        } catch (backupCheckError) {
          console.error('检查备份数据失败:', backupCheckError)
        }
        // 使用一个简单的API调用来获取服务器时间
        const response = await Taro.cloud.callFunction({
          name: 'totpGenerator',
          data: { action: 'getTimeInfo' }
        })

        if (response && response.result && response.result.success) {
          const { currentTime } = response.result.data
          const localTime = Math.floor(Date.now() / 1000)
          const offset = currentTime - localTime

          // 保存到本地存储
          Taro.setStorageSync('serverTimeOffset', offset.toString())
          setServerTimeOffset(offset)
          console.log(`初始化时同步服务器时间，偏移: ${offset}秒`)
        }
      } catch (error) {
        console.error('同步服务器时间失败:', error)
        // 出现异常时，尝试从头初始化
        try {
          Taro.removeStorageSync('serverTimeOffset')
          Taro.removeStorageSync('totpAccounts')
        } catch (clearError) {
          console.error('清除异常缓存失败:', clearError)
        }
      }
    }

    // 启动时进行一次时间同步
    syncTime()

    // 每10分钟同步一次时间，以保持精确
    const timeSync = setInterval(syncTime, 600000)

    return () => clearInterval(timeSync)
  }, [])

  return (
    <View className='totp-generator'>
      <View className='header'>
        <OtherPay size='50' color='#4338ca' />
        <Text className='title'>验证码生成器</Text>
        <Text className='description'>生成动态验证码，保护您的账户安全</Text>
      </View>

      {/* 调试信息区域 */}
      {/* <View
        style={{
          padding: '10px',
          backgroundColor: '#f0f0f0',
          borderRadius: '5px',
          marginBottom: '10px',
          fontSize: '12px'
        }}>
        <Text style={{ color: '#666' }}>账户数量: {accounts.length}</Text>
        <View style={{ color: '#666', wordBreak: 'break-all' }}>验证码: {JSON.stringify(currentCodes)}</View>
      </View> */}

      <View className='timer-container'>
        <View
          className={`timer-circle ${isLoading && Object.keys(currentCodes).length === 0 ? 'loading-circle' : ''}`}
          ref={circleRef}
          style={{ '--progress': `${(timeRemaining / 30) * 100}%` }}
          onClick={handleRefresh}>
          <Text className='timer-text'>
            {isLoading && Object.keys(currentCodes).length === 0 ? '...' : timeRemaining}
          </Text>
        </View>
      </View>

      <View className='accounts-container'>{renderAccounts()}</View>

      <View className='action-buttons'>
        <Button className='action-btn add-btn' onClick={() => setShowAddDialog(true)}>
          <Text>添加账号</Text>
        </Button>
        <Button className='action-btn scan-btn' onClick={handleScanQR}>
          <Scan />
          <Text>扫描二维码</Text>
        </Button>
        {renderRefreshButton()}
      </View>

      <View style={{ marginTop: '10px', padding: '10px' }}>
        <Button
          className='action-btn clear-btn'
          onClick={async () => {
            try {
              // 清除本地存储
              Taro.removeStorageSync('totpAccounts')

              // 清除云端数据
              try {
                await Taro.cloud.callFunction({
                  name: 'totpGenerator',
                  data: {
                    action: 'clearAccounts'
                  }
                })
                console.log('云端账户数据已清除')
              } catch (cloudError) {
                console.error('清除云端账户失败:', cloudError)
                // 即使云端清除失败，我们仍然清除本地账户
              }

              // 重置状态
              setAccounts([])
              setCurrentCodes({})
              showToast('已清除所有账户（本地和云端）', 'success')
            } catch (error) {
              console.error('清除账户失败:', error)
              showToast('清除账户失败', 'error')
            }
          }}>
          清除所有账户
        </Button>
      </View>

      {/* 添加账户对话框 */}
      <Dialog open={showAddDialog} onClose={() => setShowAddDialog(false)}>
        <Dialog.Header>添加账号</Dialog.Header>
        <Dialog.Content>
          <Cell.Group inset>
            <Cell title='账户名称'>
              <Input
                className='dialog-input'
                placeholder='例如: 工作邮箱'
                value={newAccount.name}
                onInput={(e) => setNewAccount({ ...newAccount, name: e.detail.value })}
              />
            </Cell>
            <Cell title='密钥'>
              <Input
                className='dialog-input'
                placeholder='例如: ABCDEF123456'
                value={newAccount.secret}
                onInput={(e) => setNewAccount({ ...newAccount, secret: e.detail.value })}
              />
            </Cell>
            <Cell title='颁发者 (可选)'>
              <Input
                className='dialog-input'
                placeholder='例如: Google'
                value={newAccount.issuer}
                onInput={(e) => setNewAccount({ ...newAccount, issuer: e.detail.value })}
              />
            </Cell>
          </Cell.Group>
        </Dialog.Content>
        <Dialog.Actions>
          <Button className='btn-flex' onClick={() => setShowAddDialog(false)}>
            取消
          </Button>
          <Button className='btn-flex' onClick={handleAddAccount}>
            添加
          </Button>
        </Dialog.Actions>
      </Dialog>

      {/* 编辑账户对话框 */}
      <Dialog open={showEditDialog} onClose={() => setShowEditDialog(false)}>
        <Dialog.Header>编辑验证器账户</Dialog.Header>
        <Dialog.Content>
          {accountToEdit && (
            <Cell.Group inset>
              <Cell title='账户名称'>
                <Input
                  className='dialog-input'
                  value={accountToEdit.name}
                  onInput={(e) => setAccountToEdit({ ...accountToEdit, name: e.detail.value })}
                />
              </Cell>
              <Cell title='密钥'>
                <Input
                  className='dialog-input'
                  value={accountToEdit.secret}
                  onInput={(e) => setAccountToEdit({ ...accountToEdit, secret: e.detail.value })}
                />
              </Cell>
              <Cell title='颁发者 (可选)'>
                <Input
                  className='dialog-input'
                  value={accountToEdit.issuer}
                  onInput={(e) => setAccountToEdit({ ...accountToEdit, issuer: e.detail.value })}
                />
              </Cell>
            </Cell.Group>
          )}
        </Dialog.Content>
        <Dialog.Actions>
          <Button onClick={() => setShowEditDialog(false)}>取消</Button>
          <Button type='primary' onClick={confirmEditAccount}>
            保存
          </Button>
        </Dialog.Actions>
      </Dialog>

      {/* 删除确认对话框 */}
      <Dialog open={showDeleteDialog} onClose={() => setShowDeleteDialog(false)}>
        <Dialog.Header>删除账户</Dialog.Header>
        <Dialog.Content>
          {accountToDelete && <Text>确定要删除账户 &quot;{accountToDelete.name}&quot; 吗？此操作无法恢复。</Text>}
        </Dialog.Content>
        <Dialog.Actions>
          <Button onClick={() => setShowDeleteDialog(false)}>取消</Button>
          <Button type='primary' color='danger' onClick={confirmDeleteAccount}>
            删除
          </Button>
        </Dialog.Actions>
      </Dialog>

      {/* Toast提示 */}
      <Toast
        open={toastConfig.open}
        type={toastConfig.type}
        onClose={() => setToastConfig({ ...toastConfig, open: false })}>
        {toastConfig.message}
      </Toast>
    </View>
  )
}

export default TotpGenerator
